Created by Ignasi 'Iggy' Bosch / @ignasibosch
Noble - Be humble and responsable *are not for free
Art - Be wise and clever *use it correctly
Cheating - It's about to create lies *you can end up believing your own lies
SUT - System under test
(a.k.a.: Object-under-test, Component Under Test (CUT))
DOC - Depended-on component
(a.k.a.: Collaborator, Depenency)
Black Box Testing, Functional Testing, Performance Testing, Regression Testing, Smoke Testing, Sanity Testing, Parallel Testing, Recovery Testing, Installation Testing, Compatibility Testing, Configuration Testing, Compliance Testing, Error-Handling Testing, Manual-Support Testing, Inter-Systems Testing, Exploratory Testing, Volume Testing, Scenario Testing, User Interface Testing, System Testing, User Acceptance Testing, Alpha Testing, Beta Testing, White Box Testing, Unit Testing, Static and Dynamic Analysis, Statement Coverage, Decision Coverage, Condition Coverage, Path Coverage, Integration Testing, Bottom-Up Integration Testing, Top-Down Integration Testing, Security Testing, Mutation Testing, Stress Testing, Resilience Testing ...
“The term Test Double as the generic term for any kind of pretend object used in place of a real object for testing purposes. The name comes from the notion of a Stunt Double in movies.”
(Mocks Aren't Stubs)
Martin Fowler
public void testInvoice_addLineItem_DO() {
final int QUANTITY = 1;
// Setup
Product product = new Product("Dummy Product Name", getUniqueNumber());
Invoice sut = new Invoice( new DummyCustomer() );
LineItem expItem = new LineItem(sut, product, QUANTITY);
// Exercise
sut.addItemQuantity(product, QUANTITY);
// Verify
List lineItems = sut.getLineItems();
assertEquals("number of items", lineItems.size(), 1);
LineItem actual = (LineItem)lineItems.get(0);
assertLineItemsEqual("", expItem, actual);
}
public class DummyCustomer implements ICustomer {
public DummyCustomer() {
// Real simple; nothing to initialize!
}
public int getZone() {
throw new RuntimeException("This should never be called!");
}
}
public void testDisplayCurrentTime_AtMidnight() throws Exception {
// Setup
TimeProviderTestStub tpStub = new TimeProviderTestStub();
tpStub.setHours(0);
tpStub.setMinutes(0);
// Instantiate SUT
TimeDisplay sut = new TimeDisplay();
// Test Double installation
sut.setTimeProvider(tpStub);
// Exercise SUT
String result = sut.getCurrentTimeAsHtmlFragment();
// Verify outcome
String expectedTimeString = "<span class=\"tinyBoldText\">Midnight</span>";
assertEquals("Midnight", expectedTimeString, result);
}
class TimeProviderTestStub implements TimeProvider {
// Configuration Interface
public void setHours(int hours) {
// 0 is midnight; 12 is noon
myTime.set(Calendar.HOUR_OF_DAY, hours);
}
public void setMinutes(int minutes) {
myTime.set(Calendar.MINUTE, minutes);
}
// Interface Used by SUT
public Calendar getTime() {
// @return the last time that was set
return myTime;
}
}
public void testRemoveFlightLogging_recordingTestStub() throws Exception {
// fixture setup
FlightDto expectedFlightDto = createAnUnregFlight();
FlightManagementFacade sut = new FlightManagementFacadeImpl();
// Test Double setup
AuditLogSpy logSpy = new AuditLogSpy();
sut.setAuditLog(logSpy);
// exercise
sut.removeFlight(expectedFlightDto.getFlightNumber());
// verify
assertFalse("flight still exists after being removed",
sut.flightExists( expectedFlightDto.getFlightNumber()));
assertEquals("number of calls", 1, logSpy.getNumberOfCalls());
assertEquals("action code", Helper.REMOVE_FLIGHT_ACTION_CODE, logSpy.getActionCode());
assertEquals("date", helper.getTodaysDateWithoutTime(), logSpy.getDate());
assertEquals("user", Helper.TEST_USER_NAME, logSpy.getUser());
assertEquals("detail", expectedFlightDto.getFlightNumber(), logSpy.getDetail());
}
public class AuditLogSpy implements AuditLog {
// Fields into which we record actual usage information
private Date date;
private String user;
private String actionCode;
private Object detail;
private int numberOfCalls = 0;
// Recording implementation of real AuditLog interface
public void logMessage(Date date, String user, String actionCode, Object detail) {
this.date = date;
this.user = user;
this.actionCode = actionCode;
this.detail = detail;
numberOfCalls++;
}
// Retrieval Interface
public int getNumberOfCalls() {
return numberOfCalls;
}
public Date getDate() {
return date;
}
public String getUser() {
return user;
}
public String getActionCode() {
return actionCode;
}
public Object getDetail() {
return detail;
}
}
public void testRemoveFlight_JMock() throws Exception {
// fixture setup
FlightDto expectedFlightDto = createAnUnregFlight();
FlightManagementFacade sut = new FlightManagementFacadeImpl();
// mock configuration
Mock mockLog = mock(AuditLog.class);
mockLog.expects(once())
.method("logMessage")
.with(
eq(helper.getTodaysDateWithoutTime()),
eq(Helper.TEST_USER_NAME),
eq(Helper.REMOVE_FLIGHT_ACTION_CODE),
eq(expectedFlightDto.getFlightNumber())
);
// mock installation
sut.setAuditLog((AuditLog) mockLog.proxy());
// exercise
sut.removeFlight(expectedFlightDto.getFlightNumber());
// verify
assertFalse("flight still exists after being removed",
sut.flightExists( expectedFlightDto.getFlightNumber()));
// verify() method called automatically by JMock
}
public void testReadWrite_inMemory() throws Exception{
// Setup
FlightMgmtFacadeImpl sut = new FlightMgmtFacadeImpl();
sut.setDao(new InMemoryDatabase());
BigDecimal yyc = sut.createAirport("YYC", "Calgary", "Calgary");
BigDecimal lax = sut.createAirport("LAX", "LAX Intl", "LA");
sut.createFlight(yyc, lax);
// Exercise
List flights = sut.getFlightsByOriginAirport(yyc);
// Verify
assertEquals( "# of flights", 1, flights.size());
Flight flight = (Flight) flights.get(0);
assertEquals( "origin", yyc, flight.getOrigin().getCode());
}
public class InMemoryDatabase implements FlightDao{
private List airports = new Vector();
public Airport createAirport(String airportCode, String name, String nearbyCity)
throws DataException, InvalidArgumentException {
assertParamtersAreValid( airportCode, name, nearbyCity);
assertAirportDoesntExist( airportCode);
Airport result = new Airport(getNextAirportId(),
airportCode, name, createCity(nearbyCity));
airports.add(result);
return result;
}
public Airport getAirportByPrimaryKey(BigDecimal airportId)
throws DataException, InvalidArgumentException {
assertAirportNotNull(airportId);
Airport result = null;
Iterator i = airports.iterator();
while (i.hasNext()) {
Airport airport = (Airport) i.next();
if (airport.getId().equals(airportId)) {
return airport;
}
}
throw new DataException("Airport not found:" + airportId);
}
}
Test Double Patterns
Martin Fowler: Mocks Aren't Stubs
Unit Testing: Mocks, Stubs and Spies
Steve Hostettler: Fakes, Stubs, Dummy, Mocks, Doubles and All That...
Martin Fowler: Eradicating Non-Determinism in Tests
Types of software Testing
Mocks & stubs by Ken Scambler
SymfonyLive London 2014 - Dave Marshall - Mocks Aren't Stubs, Fakes, Dummies or Spies